#pragma once
#ifndef  __M_LOG_H_
#define  __M_LOG_H_

#include <iostream>
#include <string>
#include <filesystem>
#include<sstream>
#include <fstream>
#include "mutex.hpp"
#include <memory>
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>

using namespace MutexModule;
namespace logmodule
{
    const std::string gesp = "\r\n";
    //刷新策略：向显示器，向指定的文件写入
    class logstrategy
    {
        public:
        logstrategy()
        {}
        ~logstrategy() = default;
        virtual void logsink(const std::string &message )= 0;
    };
    //显示器打印日志的策略
    class Consolelogstrategy:public logstrategy
    {
        public:
        Consolelogstrategy()
        {}
        ~Consolelogstrategy()
        {}
        virtual void logsink(const std::string &message )
        {
            lockguard sinklock(_mutex);
            std::cout<<message<<"\r\n";
        }
        private:
        Mutex _mutex;
    };
    //文件打印日志的策略
    const std::string defaultpath = "./log";
    const std::string defaultfilename = "mylog.log";
    class filelogstrategy:public logstrategy
    {
        public:
        filelogstrategy(const std::string &path=defaultpath,const std::string &filename = defaultfilename)
        :_path(path)
        ,_filename(filename)
        {
            if(std::filesystem::exists(_path))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch(const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
            
        }
        virtual void logsink(const std::string &message )
        {
            std::string realfilename = _path+(_path.back()=='/'?"":"/")+_filename;
            std::ofstream out(realfilename,std::ios::app);//以追加写入的方式来打开文件
            if(!out.is_open())
            {
                return ;
            }
            out<<message<<gesp;

            out.close();
        }
        private:
        std:: string _path;//文件存储的路径
        std::string _filename;//日志文件本身
        Mutex _mutex;
    };
    //形成完整日志
    //等级枚举常量
    enum class loglevel
    {
        DEBUG,
        INFO,
        WARN,
        ERROR,
        FATAL
    };
    std::string leveltostr(loglevel level)
    {
        switch (level)
        {
        case loglevel::DEBUG:
        return "DEBUG";
        case loglevel::INFO:
        return "INFO";
        case loglevel::WARN:
        return "WARN";
        case loglevel::ERROR:
        return "ERROR";
        case loglevel::FATAL:
        return "FATAL";
        default:
        return "UNKNOWN";
        }

    }

    std::string gettime()
    {
        time_t currtime  = time(nullptr);
        struct tm cur_tm;
        localtime_r(&currtime,&cur_tm);
        char timebuffer[128];
        snprintf(timebuffer,sizeof(timebuffer),"%d-%02d-%d %d:%d:%d",
    cur_tm.tm_year+1900,
    cur_tm.tm_mon+1,
    cur_tm.tm_mday,
    cur_tm.tm_hour,
    cur_tm.tm_min,
    cur_tm.tm_sec);
    return timebuffer;
    }



    //形成日治
    class logger
    {
        public:
        logger()
        {
            enableconsole();
        }
        void enablefile()
        {
            _flush_strategy = std::make_unique<filelogstrategy>();
        }
        void enableconsole()
        {
            _flush_strategy = std::make_unique<Consolelogstrategy>();
        }
        class logmessage
        {
            public:
            logmessage(loglevel &level,std::string &src_name,int line_number,logger &loggerr)
            :_cur_time(gettime()),
            _level(level),
            _pid(getpid()),
            _src_name(src_name),
            _line_number(line_number),
            _logger(loggerr)
            {
                //将日志的左半部分合并起来
                std::stringstream ss;
                ss<<"["<<_cur_time<<"]"
                  <<"["<<leveltostr(_level)<<"]"
                  <<"["<<_pid<<"]"
                  <<"["<<_src_name<<"]"
                  <<"["<<_line_number<<"]"
                  <<"-";
                  _logdetail  = ss.str();
            }
            template<typename T>
            logmessage &operator <<(const T &info)
            {
                //日志的右边可变部分
                std::stringstream ss;
                ss<<info;
                _logdetail+=ss.str();
                return *this; 
            }    
            ~logmessage()
            {
                if(_logger._flush_strategy)
                {
                    _logger._flush_strategy->logsink(_logdetail);
                }
            }
            private:
            std::string _cur_time;
            loglevel _level;
            pid_t _pid;
            std::string _src_name;
            int _line_number;
            std::string _logdetail;//合并后的完整日志信息
            logger &_logger;

        };
        logmessage operator() (loglevel level,std::string src_name,int line_number)
        {
            return logmessage(level,src_name,line_number,*this);
        }
        ~logger()
        {}
        private:
        std::unique_ptr<logstrategy> _flush_strategy;

    };

    logger log;
    //再进一步使用宏简化用户操作
    #define LOG(level) log(level,__FILE__,__LINE__)//目标文件的文件名和行号
    #define EnableCon() log.enableconsole()
    #define Enablefile() log.enablefile()
}
#endif